﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Data.SqlClient;
using System.IO;

namespace For_FS_2013_1_User
{
    class UserConnection
    {
        private TcpClient client;
        private TcpListener listener;
        private string userLogin = null;
        private int port;
        private Thread receive;
        private Thread connect;
        private bool stopReceive;
        private Socket fileSocket;

        public UserConnection(int port)
        {
            this.port = port;
            fileSocket = null;
            stopReceive = false;
            listener = new TcpListener(IPAddress.Any, port);
            listener.Start();
            Start();
        }

        private void CreateThreads()
        {
            client = new TcpClient();
            connect = new Thread(Connect);
            receive = new Thread(ReceiveMessage);
        }

        private void Start()
        {
            CreateThreads();
            connect.Start();
        }

        private void Connect()
        {
            while (!client.Connected)
            {
                client = listener.AcceptTcpClient();
            }
            receive.Start();
            Console.WriteLine("User has been reconnected to the "+port+" port!!!");
        }

        private void Disconnect()
        {
            stopReceive = true;
            if (client != null)
                client.Close();
            listener.Stop();
            userLogin = null;
            Program.con.DisconnectUser(this);
        }

        private void SendMessage(string message)
        {
            if (client.Connected)
            {
                NetworkStream ns = client.GetStream();
                if (ns != null)
                {
                    byte[] buffer = Encoding.Default.GetBytes(message);
                    ns.Write(buffer, 0, buffer.Length);
                }
            }
        }

        private void ReceiveMessage()
        {
            string request = "";
            while (!stopReceive)
            {
                try
                {
                    if (client.Connected)
                    {
                        NetworkStream ns = client.GetStream();
                        while (ns.DataAvailable)
                        {
                            byte[] buffer = new byte[client.Available];
                            ns.Read(buffer, 0, buffer.Length);
                            request += Encoding.Default.GetString(buffer);
                            //Console.WriteLine(userLogin + "->" + request);
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                if (request != "")
                {
                    Console.WriteLine("User->" + request); //////////////////////////
                    ExecuteUserRequest(request);
                    request = "";
                }
                Thread.Sleep(100);
            }
        }

        private void ConnectFileReceiver()
        {
            IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, port+1);
            Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            sock.Bind(ipEnd);
            sock.Listen(Int32.MaxValue);
            while (fileSocket == null)
                fileSocket = sock.Accept();
            sock.Close();
            sock = null;
            Console.WriteLine("File Receiver Connected!!!");
        }

        private void DisconnectFileReceiver()
        {
            fileSocket.Shutdown(SocketShutdown.Both);
            fileSocket.Close();
            fileSocket = null;
        }

        private HashSet<FileDescription> getFilesInfo(List<string> files)
        {
            HashSet<FileDescription> result = new HashSet<FileDescription>();
            foreach (string file in files)
            {
                SqlDataReader dr = Program.users.ExecuteSQLCommand("Select Name, Path from Files where Name='" + file + "' and AvailableForUser=1");
                while (dr.Read())
                {
                    result.Add(new FileDescription(dr["Name"].ToString(), dr["Path"].ToString()));
                }
                dr.Close();
            }
            return result;
        }

        private void SendFile(object data)
        {
             HashSet<FileDescription> files = getFilesInfo((List<string>)data);
            foreach (FileDescription f in files)
            {
                ConnectFileReceiver();

                byte[] fileNameByte = Encoding.ASCII.GetBytes(f.name);
                byte[] fileData = File.ReadAllBytes(f.path + f.name);
                byte[] clientData = new byte[4 + fileNameByte.Length + fileData.Length];
                byte[] fileNameLen = BitConverter.GetBytes(fileNameByte.Length);

                fileNameLen.CopyTo(clientData, 0);
                fileNameByte.CopyTo(clientData, 4);
                fileData.CopyTo(clientData, 4 + fileNameByte.Length);

                int k = 0;
                while (k < clientData.Length)
                    k += fileSocket.Send(clientData);
                Console.WriteLine("File:{0} has been sent from port {1} to the client", f.name, port+1);

                DisconnectFileReceiver();
            }
        }

        private void ExecuteUserRequest(string request)
        {
            string[] task=request.Split(' ');
            if (task[0] == "login")
            {
                userLogin=Program.users.AuthorizeUser(task[1], task[2]);
                if (userLogin != null)
                {
                    if (Program.users.IsAdmin(userLogin))
                    {
                        Console.WriteLine("User authorized as " + userLogin + " to the " + port + " port!!!");
                        SendMessage("login true true");
                    }
                    else
                    {
                        SendMessage("login true false");
                    }
                }
                else
                {
                    SendMessage("login false false");
                    Disconnect();
                }
                return;
            }
            if (task[0] == "disconnect")
            {
                //Console.WriteLine("User "+userLogin+" disconnected from the server port->" + port);
                Console.WriteLine("User disconnected from the server port->" + port); ////////////////////
                Disconnect();
                return;
            }
            if (task[0] == "send_files")
            {
                List<string> files = new List<string>();
                string[] words = request.Split('*');
                for (int i = 1; i < words.Length; i++)
                    files.Add(words[i]);
                Thread sf = new Thread(SendFile);
                sf.Start(files);
                    return;
            }
            if (task[0] == "get_file_list")
            {
                HashSet<string> files = Program.fm.GetFiles();
                string result=task[0]+" *";
                foreach (string s in files)
                    result += s + "*";
                SendMessage(result);
            }
        }

        #region Get login and port

        public string GetUserLogin()
        {
            return userLogin;
        }

        public int GetPort()
        {
            return port;
        }
        #endregion
    }
}
